package com.hup.utils.commons.start;

import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.exception.ErrInfo;
import com.hup.utils.commons.hardware.ScreenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.*;

public class DialogHelper {
    private static final Logger log = LoggerFactory.getLogger(DialogHelper.class);
    public static final String INFO = "消息";
    public static final String WARN = "警告";
    public static final String ERROR = "错误";

    private static MyJDialog CONFIRM_DIALOG;
    private static MyJDialog YES_NO_DIALOG;

    private MyJDialog dialog;

    private DialogHelper(MyJDialog dialog) {
        this.dialog = dialog;
    }

    public static ErrInfo show(String desc, Throwable e, Window parent) {
        ErrInfo errInfo = AppExceptionHandler.handle(desc, e);
        confirm(errInfo.getErrMsg()).title(ERROR).show(parent);
        return errInfo;
    }

    private static void initConfirm() {
        if (CONFIRM_DIALOG != null) return;
        MyJDialog dialog = new MyJDialog();
        //dialog.setModal(false);//不阻塞当前线程
        dialog.taMsg.setLineWrap(true);
        dialog.buttonCancel.setVisible(false);
        dialog.cbLineWrap.setVisible(false);
        CONFIRM_DIALOG = dialog;
    }

    private static void initYesNo() {
        if (YES_NO_DIALOG != null) return;
        MyJDialog dialog = new MyJDialog();
        //dialog.setModal(false);//不阻塞当前线程
        dialog.taMsg.setLineWrap(true);
        dialog.buttonCancel.setVisible(true);
        dialog.cbLineWrap.setVisible(false);
        YES_NO_DIALOG = dialog;
    }

    private static MyJDialog initErrExit() {
        MyJDialog dialog = new MyJDialog();
        dialog.setModal(true);//阻塞当前线程
        dialog.buttonCancel.setVisible(false);
        dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        return dialog;
    }

    public static DialogHelper confirm(String msg) {
        initConfirm();
        DialogHelper helper = new DialogHelper(CONFIRM_DIALOG);
        helper.dialog.setTitle(INFO);
        helper.dialog.taMsg.setText(msg);
        helper.dialog.setAlwaysOnTop(false);
        helper.dialog.hadChangedOnTop = false;
        helper.dialog.okListener.setAction(null);
        helper.dialog.cancelAction = null;
        return helper;
    }

    public static DialogHelper yesNo(String msg) {
        initYesNo();
        DialogHelper helper = new DialogHelper(YES_NO_DIALOG);
        helper.dialog.setTitle(INFO);
        helper.dialog.taMsg.setText(msg);
        helper.dialog.setAlwaysOnTop(false);
        helper.dialog.hadChangedOnTop = false;
        helper.dialog.okListener.setAction(null);
        helper.dialog.cancelAction = null;
        return helper;
    }

    public static DialogHelper errExit(String msg) {
        DialogHelper helper = new DialogHelper(initErrExit());
        helper.dialog.setTitle(ERROR);
        helper.dialog.taMsg.setText(msg);
        helper.dialog.setAlwaysOnTop(false);
        helper.dialog.hadChangedOnTop = false;
        helper.dialog.okListener.setAction(() -> System.exit(0));
        helper.dialog.cancelAction = null;
        return helper;
    }

    @SuppressWarnings("SuspiciousNameCombination")
    private static EmptyBorder emptyBorder(int width) {
        return new EmptyBorder(width, width, width, width);
    }

    public DialogHelper warn() {
        dialog.setTitle(WARN);
        return this;
    }

    public DialogHelper error() {
        dialog.setTitle(ERROR);
        return this;
    }

    public DialogHelper title(String title) {
        dialog.setTitle(title);
        return this;
    }

    public DialogHelper onTop(boolean onTop) {
        dialog.setAlwaysOnTop(onTop);
        dialog.hadChangedOnTop = true;
        return this;
    }

    public DialogHelper okAction(Runnable okAction) {
        dialog.okListener.setAction(okAction);
        return this;
    }

    public DialogHelper cancelAction(Runnable cancelAction) {
        dialog.cancelAction = cancelAction;
        return this;
    }

    /**
     * @return 是否按下[ok]
     */
    public boolean show() {
        return show(null);
    }

    /**
     * @return 是否按下[ok]
     */
    public boolean show(Window parent) {
        //还原文本位置
        dialog.taMsg.setSelectionStart(0);
        dialog.taMsg.setSelectionEnd(0);

        //自适应尺寸
        Dimension sizeOfText = getSizeByWrapText(dialog.taMsg, dialog.taMsg.getText(), 10);
        /*
        20是滚动条宽度,第一次展示时,获取到的滚动条宽度是0,所以要写死
        还要加上额外的距离,否则有可能测量出来的宽度仍然不够
         */
        sizeOfText.width += 38;
        sizeOfText.height += 10;
        JScrollPane scrollPane = (JScrollPane) dialog.taMsg.getParent().getParent();
        scrollPane.setPreferredSize(sizeOfText);
        log.debug("sizeOfText={}", sizeOfText);
        dialog.pack();
        dialog.setLocationRelativeTo(parent);
        if (!dialog.hadChangedOnTop && parent != null && parent.isAlwaysOnTop()) {
            dialog.setAlwaysOnTop(true);
        }
        dialog.choiceResult = MyJDialog.CHOICE_EXIT_DIALOG;
        dialog.setVisible(true);
        return dialog.choiceResult == MyJDialog.CHOICE_OK;
    }

    private static class MyJDialog extends JDialog {
        private static final long serialVersionUID = 751843853638184879L;
        private JPanel contentPane;
        private JButton buttonOK;
        private JButton buttonCancel;
        private JTextArea taMsg;
        private JCheckBox cbLineWrap;
        private MyActionListener okListener;
        public Runnable cancelAction;

        /**
         * 点击关闭窗口按钮
         */
        private static final int CHOICE_EXIT_DIALOG = 0;
        /**
         * 点击取消按钮
         */
        private static final int CHOICE_CANCEL = -1;
        /**
         * 点击确认按钮
         */
        private static final int CHOICE_OK = 1;
        private int choiceResult;
        public boolean hadChangedOnTop;

        private MyJDialog() {
            setupUI();
            setContentPane(contentPane);
            setModal(true);
            getRootPane().setDefaultButton(buttonOK);
            cbLineWrap.addActionListener(e -> taMsg.setLineWrap(cbLineWrap.isSelected()));

            okListener = new MyActionListener(this);
            buttonOK.addActionListener(okListener);

            buttonCancel.addActionListener(e -> {
                choiceResult = CHOICE_CANCEL;
                dispose();
            });
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentShown(ComponentEvent e) {
                    //这样才能使按钮第一时间获取焦点,实现空格确认
                    //taMsg.setFocusable(false);会使错误栏无法用鼠标选择
                    buttonOK.requestFocus();
                }

                @Override
                public void componentHidden(ComponentEvent e) {
                    afterChoice();
                }
            });
            addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent e) {
                    afterChoice();
                }
            });
        }

        private void setupUI() {
            contentPane = new JPanel(new BorderLayout());
            contentPane.setBorder(new EmptyBorder(10, 10, 0, 10));

            JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            contentPane.add(panel1, BorderLayout.SOUTH);

            panel1.add(cbLineWrap = new JCheckBox("换行"));
            panel1.add(buttonOK = new JButton("确定"));
            panel1.add(buttonCancel = new JButton("取消"));

            JScrollPane scrollPane1 = new JScrollPane();
            contentPane.add(scrollPane1, BorderLayout.CENTER);
            taMsg = new JTextArea();
            taMsg.setEditable(false);
            taMsg.setEnabled(true);
            taMsg.setLineWrap(false);
            taMsg.setOpaque(false);
            taMsg.setBorder(emptyBorder(10));
            taMsg.setText("这是消息");
            scrollPane1.setViewportView(this.taMsg);
        }

        private void afterChoice() {
            if (choiceResult != CHOICE_OK && cancelAction != null) {
                cancelAction.run();
            }
        }

    }

    private static class MyActionListener implements ActionListener {

        private final MyJDialog dialog;
        private Runnable action;

        private MyActionListener(MyJDialog dialog) {
            this.dialog = dialog;
        }

        private void setAction(Runnable action) {
            this.action = action;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            //log.info("title={}, 有action={}", dialog.getTitle(), action != null);
            try {
                if (action != null) action.run();
            } catch (Exception err) {
                String msg = AppExceptionHandler.handle(err).toString();
                log.error("操作错误={}", msg);
                JOptionPane.showConfirmDialog(dialog, msg,
                        ERROR,
                        JOptionPane.DEFAULT_OPTION,
                        JOptionPane.ERROR_MESSAGE
                );
            } finally {
                dialog.choiceResult = MyJDialog.CHOICE_OK;
                dialog.dispose();
            }
        }
    }


    /**
     * 根据传入字符串设置控件大小
     *
     * @param str 以'\n'为换行符的字符串
     * @param maxLine 最大行数,用于定义最大高度
     * @return 控件大小, 包括insert!
     */
    @SuppressWarnings("Duplicates")
    private static Dimension getSizeByWrapText(JTextComponent c, String str, int maxLine) {
        FontMetrics metrics = c.getFontMetrics(c.getFont());
        String[] lines = str.split("\n");
        String longestLine = lines[0];
        for (String line : lines) {
            if (longestLine.length() < line.length()) {
                longestLine = line;
            }
        }
        int lineCount = lines.length;
        int width = metrics.stringWidth(longestLine);
        int maxWid = ScreenUtil.getScreenSize().width / 3 * 2;
        if (width > maxWid) {
            //限制最大宽度
            width = maxWid;
            //限制宽度后,会换行:最小行数为5行
            lineCount = Math.max(lineCount, 5);
        }
        int height = metrics.getHeight();//单行高度
        height = height * Math.min(lineCount, maxLine);
        Insets insets = c.getInsets();
        return new Dimension(insets.left + width + insets.right,
                insets.top + height + insets.bottom);
    }

}
